import jwt from 'jsonwebtoken';
import querystring from 'querystring';
import {
  WX_MINIPROGRAM_APP_ID,
  WX_MINIPROGRAM_SECRET,
  JWT_SECRET,
  FAKE_VERTIFICATION,
  TOKEN_EXPIRED_TIME
} from '../config';
import shortid from 'shortid';
import moment from 'moment';
import crypto from 'crypto';
import Identicon from 'identicon.js';
import validator from 'validator';
import pick from 'lodash/pick';
import {
  User,
  BookList,
  Pay,
  Share,
  Attendance,
  Award,
  Comment,
  Setting,
  Notice,
  Member,
  Logger
} from '../models';
import { reportError, doRequest } from '../utils';
import redis from '../utils/redis';
import wechat from '../utils/weixin';
import sendSmsMessage from '../utils/sendMsg';
import uploadToTencentCos from '../utils/txCosUpload';
import { transPasswordToHash } from '../utils/password';

const createToken = (user, expiresIn) => {
  const { _id, identity } = user;
  return jwt.sign({ userid: _id, identity }, JWT_SECRET, {
    expiresIn
  });
};

// console.log(createToken({ _id: '5f5efcb82064967e5c6fc7c4', identity: 1 }, TOKEN_EXPIRED_TIME));

const formatUserOutput = async user => {
  // 获取用户分享唯一标识
  const shareCode = await Share.getUserShareCode(user._id);
  // 是否设置了登录密码
  const hasPassword = await user.checkPassword('') ? false : true;

  return {
    userid: user._id,
    username: user.username,
    mobile: user.mobile ? user.mobile.replace(/^(\d{3})(\d{4})(\d{4})$/, '$1****$2') : '',
    avatar: user.avatar,
    identity: user.identity,
    amount: user.amount,
    shareCode,
    hasPassword,
  };
};

const defautUserTemplate = data => {
  return {
    username: 'unknow', // 用户名就使用昵称
    password: '',
    avatar: 'https://picture-1256514897.file.myqcloud.com/book/codes/default-avatar.png',
    identity: 1, // 区分用户是普通用户还是系统管理员
    source: 'unknow',
    amount: 0, //
    setting: {
      updateNotice: true,
      autoBuy: true,
      reader: {
        fontSize: 36,
        fontFamily: '使用系统字体',
        bright: 1,
        mode: '默认', // 模式,
        overPage: 1 // 翻页模式
      }
    },
    read_time: 0,
    create_time: new Date(),
    ...data
  };
};

/**
 * 更新用户头像
 * @param {string} userid 用户ID
 * @param {string} avatar 头像地址
 * @param {string} type 类型，http网络地址，base64 表示编码图片
 */
function updateUserAvatar(userid, avatar, type) {
  if (!avatar) return;
  uploadToTencentCos({
    Bucket: 'picture',
    Body: avatar,
    Type: type,
    Key: 'mbook/avatar/' + userid + '.png',
    useCdn: true
  }).then(res => {
    // 更新用户最近登录时间，并将登录次数加1
    User.updateOne({ _id: userid }, { $set: { avatar: res } }, function (err) {
      if (err) {
        reportError('更新用户头像失败', err, {
          priority: '高',
          category: '错误',
          extra: { userid, avatar }
        });
        return false;
      }
    });
  });
}

/**
 * 初始化用户书架
 * @param {*} userid 用户ID
 */
function initUserBooklist(userid) {
  BookList.create({
    userid: userid,
    books: []
  })
    .then(() => {
      console.log(`初始化用户${userid}书架成功`);
    })
    .catch(err => {
      reportError('初始化用户书架失败', err, {
        priority: '高',
        category: '错误',
        extra: { userid }
      });
    });
}

async function getGlobalSetting() {
  let items = ['share', 'wxcode', 'index_dialog', 'charge_tips', 'secret_tips', 'shut_charge_tips', 'fixed_button', 'friend_help_share', 'share_white_list'];
  return await await Setting.getSetting(items.join('|'));
}

export default function (router) {
  /**
   * 前端获取app设置信息
   * @method get
   */
  router.get('/api/front/user/setting', async ctx => {
    if (!ctx.state.user) return;
    const userid = ctx.state.user.id;
    // 查询当前用户的邀请信息，如果找不到则创建一个
    let hisShareInfo = await Share.findOne({ userid });
    if (!hisShareInfo) {
      const code = shortid.generate();
      hisShareInfo = await Share.create({
        userid: ctx.transId(userid),
        code,
        award_records: [],
        accept_records: [],
        create_time: new Date()
      });
    }

    // 获取最近7天通知数量
    const startDate = new Date(moment().subtract(14, 'days'));
    const endDate = new Date();
    const orParams = [];
    orParams.push({ user: { $regex: `.*${userid}.*` } });
    orParams.push({ user: 'all' });
    const userAgent = ctx.request.headers['user-agent'];
    if (/Android/i.test(userAgent)) {
      orParams.push({ user: 'android' });
    }
    if (/iPhone|iPad|iPod/i.test(userAgent)) {
      orParams.push({ user: 'ios' });
    }
    const notices = await Notice.find({ $or: orParams, create_time: { $gt: startDate, $lt: endDate } }, '_id');

    // 获取设置中的分享设置
    const globalSetting = await getGlobalSetting();
    const inShareWhiteList = globalSetting.share_white_list.indexOf(userid) > -1;
    await ctx.json(true, '获取app设置成功', {
      share: hisShareInfo,
      inShareWhiteList,
      setting: globalSetting,
      notices: notices.map(item => item._id)
    });
  });

  /**
   * 微信小程序用户判断是否注册
   */
  router.post('/api/front/user/is-registe', async ctx => {
    // 小程序登录
    const { code } = ctx.request.body;

    // 向微信服务器发送请求，使用code换取openid和session_key
    const qsdata = {
      grant_type: 'authorization_code',
      appid: WX_MINIPROGRAM_APP_ID,
      secret: WX_MINIPROGRAM_SECRET,
      js_code: code
    };
    const wxdata = await doRequest('https://api.weixin.qq.com/sns/jscode2session?' + querystring.stringify(qsdata));
    if (!wxdata || !wxdata.session_key || !wxdata.openid) {
      ctx.body = { ok: false, msg: '微信认证失败' };
      return;
    }

    // 检查用户是否已经存在
    const user = await User.findOne({ openid: wxdata.openid });
    if (!user) {
      ctx.body = { ok: true, msg: '尚注册', registe: false };
    } else {
      ctx.body = { ok: true, msg: '已注册', registe: true };
    }
  });

  /**
   * 用户登录
   * @parma {String} wey 登录方式，现在支持 miniprogram（小程序）、mp-account（公众号）、mobile+password（手机+密码）、mobile+verification（手机+验证码）、username+password（用户名+密码）
   * wey 为 miniprogram 时
   * @parma {String} code 微信临时登录凭证
   * ===========================
   * wey 为 mp-account 时
   * @parma {String} code 微信临时登录凭证
   * ===========================
   * wey 为 mobile+password 时
   * @parma {String} mobile 手机号码
   * @parma {String} password 登录密码
   * ===========================
   * wey 为 mobile+verification 时
   * @parma {String} mobile 手机号码
   * @parma {String} verification 手机校验码
   * ===========================
   * wey 为 username+password 时
   * @parma {String} username 用户名
   * @parma {String} password 登录密码
   */
  router.post('/api/front/user/login', async ctx => {
    const { wey } = ctx.request.body;
    if (wey === 'miniprogram') {
      // 小程序登录
      const { code } = ctx.request.body;

      // 向微信服务器发送请求，使用code换取openid和session_key
      const qsdata = {
        grant_type: 'authorization_code',
        appid: WX_MINIPROGRAM_APP_ID,
        secret: WX_MINIPROGRAM_SECRET,
        js_code: code
      };
      const wxdata = await doRequest('https://api.weixin.qq.com/sns/jscode2session?' + querystring.stringify(qsdata));
      if (!wxdata || !wxdata.session_key || !wxdata.openid) {
        ctx.body = { ok: false, msg: '微信认证失败' };
        return;
      }

      // 检查用户是否已经存在
      const user = await User.findOne({ openid: wxdata.openid });
      if (!user) {
        ctx.body = { ok: false, msg: '尚未注册', token: null, registe: false };
        return;
      }

      // 已注册，生成token并返回
      const token = await createToken(user, TOKEN_EXPIRED_TIME);

      // 记录日志
      console.log('用户 ' + user._id + ' 于 ' + user.create_time.toDateString() + ' 登录');
      Logger.addLog({ user: user._id, name: 'user-login' });

      ctx.body = {
        ok: true,
        msg: '登录成功',
        token: token,
        userInfo: await formatUserOutput(user)
      };
    } else if (wey === 'mp-account') {
      // 公众号登录
      const { code } = ctx.request.body;

      try {
        const baseInfo = await wechat.oauth.getUserBaseInfo(code);
        // 检查用户是否已经存在
        const user = await User.findOne({ openid: baseInfo.unionid });
        if (!user) {
          // 未注册，自动注册，并返回token
          // 获取用户信息
          const userInfo = await wechat.oauth.getUserInfoRemotely({
            access_token: baseInfo.access_token,
            openid: baseInfo.openid
          });
          const newUser = await User.create(
            defautUserTemplate({
              username: userInfo.nickname,
              avatar: userInfo.headimgurl,
              identity: 1, // 区分用户是普通用户还是系统管理员
              source: 1, // 微信公众号
              openid: baseInfo.unionid // 小程序openid
            })
          );

          // 生成token签名 有效期为一天
          const token = createToken(newUser, TOKEN_EXPIRED_TIME);

          // 初始化书架
          initUserBooklist(newUser._id);
          console.log('Info', `用户 ${newUser._id} 于 ${newUser.create_time.toDateString()} 注册, 并初始化书架`);
          // 更新最近登录时间以及头像
          updateUserAvatar(newUser._id, userInfo.headimgurl);
          // 记录日志
          Logger.addLog({ user: newUser._id, name: 'user-login' });

          ctx.body = {
            ok: true,
            msg: '登录成功',
            token: token,
            userInfo: await formatUserOutput(newUser)
          };
        } else {
          // 验证完毕，生成token
          const token = await createToken(user, TOKEN_EXPIRED_TIME);
          ctx.body = {
            ok: true,
            msg: '登录成功',
            token: token,
            userInfo: await formatUserOutput(user)
          };
        }
      } catch (err) {
        console.log(err);
        ctx.body = { ok: false, msg: '微信登录失败', err };
      }
    } else if (wey === 'mobile+password') {
      const { mobile, password } = ctx.request.body;
      // 验证参数
      const mobileReg = /^1\d{10}$/;
      if (!mobile || !mobileReg.test(mobile)) {
        ctx.body = { ok: false, error: { mobile: '手机号码格式错误' } };
        return;
      }
      if (!password || !validator.isLength(password, { min: 6, max: 30 })) {
        ctx.body = { ok: false, error: { password: '密码格式错误' } };
        return;
      }
      // 查找数据库中是否存在指定邮箱和密码的记录
      const user = await User.findOne({ mobile, identity: { $ne: 2 } });
      if (!user) {
        ctx.body = { ok: false, error: { mobile: '手机号码尚未注册' } };
        return;
      }
      // 校验密码
      const isPasswordValid = await user.checkPassword(password);
      if (!isPasswordValid) {
        ctx.body = { ok: false, error: { password: '密码错误' } };
        return;
      }

      // 验证完毕，生成token
      const token = await createToken(user, TOKEN_EXPIRED_TIME);
      ctx.body = {
        ok: true,
        msg: '登录成功',
        token: token,
        userInfo: await formatUserOutput(user)
      };
    } else if (wey === 'username+password') {
      const { username, password, identity = 1 } = ctx.request.body;
      // 验证参数
      if (!password || !validator.isLength(password, { min: 6, max: 30 })) {
        ctx.body = { ok: false, error: { password: '密码格式错误' } };
        return;
      }
      // 查找数据库中是否存在指定邮箱和密码的记录
      let user = null;
      if (+identity === 2) {
        user = await User.findOne({ username, identity: 2 });
      } else {
        user = await User.findOne({ username, identity: { $ne: 2 } });
      }
      if (!user) {
        ctx.body = { ok: false, error: { username: '此用户名尚未注册' } };
        return;
      }
      // 校验密码
      const isPasswordValid = await user.checkPassword(password);
      if (!isPasswordValid) {
        ctx.body = { ok: false, error: { password: '密码错误' } };
        return;
      }

      // 验证完毕，生成token
      const token = await createToken(user, TOKEN_EXPIRED_TIME);
      ctx.body = {
        ok: true,
        msg: '登录成功',
        token: token,
        userInfo: await formatUserOutput(user)
      };
    } else if (wey === 'mobile+verification') {
      const { mobile, verification } = ctx.request.body;
      // 手机号码是否注册过
      const user = await User.findOne({ mobile, identity: { $ne: 2 } });
      if (!user) {
        ctx.body = { ok: false, error: { mobile: '手机号码尚未注册' } };
        return;
      }
      // 验证码是否正确
      const verifyInRedis = await redis.get(`phone_verify_${mobile}`);
      if (!verifyInRedis) {
        ctx.body = { ok: false, error: { verification: '请先获取验证码' } };
        return;
      }
      if (verifyInRedis && verifyInRedis !== verification) {
        ctx.body = { ok: false, error: { verification: '验证码错误' } };
        return;
      }
      // 验证完毕，生成token
      const token = await createToken(user, TOKEN_EXPIRED_TIME);
      // 清除验证码记录
      redis.del(`phone_verify_${mobile}`);
      ctx.body = {
        code: 0,
        msg: '登录成功',
        token,
        user: await formatUserOutput(user)
      };
    } else {
      ctx.body = {
        ok: false,
        msg: '登录类型暂不支持'
      };
    }
  });

  /**
   * 发送验证码到用户手机
   * @parma {String} mobile 手机号码
   * @parma {String} usage 用来登录还是用来注册
   */
  router.post('/api/front/user/send_verify', async ctx => {
    const { usage, appName, mobile } = ctx.request.body;
    // 校验合法性
    const mobileReg = /^1\d{10}$/;
    if (!mobile || !mobileReg.test(mobile)) {
      ctx.body = { ok: false, msg: '手机号码格式错误' };
      return;
    }
    if (usage === 'login') {
      const user = await User.findOne({ mobile, identity: { $ne: 2 } });
      if (!user) {
        ctx.body = { ok: false, msg: '此号码尚未注册' };
        return;
      }
    } else if (usage === 'registe') {
      const user = await User.findOne({ mobile, identity: { $ne: 2 } });
      if (user) {
        ctx.body = { ok: false, msg: '此号码已经被注册过了，请前往登录' };
        return;
      }
    } else if (usage === 'bind-mobile') {
      const user = await User.findOne({ mobile, identity: { $ne: 2 } });
      if (user) {
        ctx.body = { ok: false, msg: '此号码已经被绑定过了，请更换新的号码' };
        return;
      }
    } else if (usage === 'change-password') {
      const user = await User.findOne({ mobile, identity: { $ne: 2 } });
      if (!user) {
        ctx.body = { ok: false, msg: '此号码尚未注册' };
        return;
      }
    }  else if (usage === 'reset-password') {
      const user = await User.findOne({ mobile, identity: { $ne: 2 } });
      if (!user) {
        ctx.body = { ok: false, msg: '此号码尚未注册' };
        return;
      }
    } else {
      ctx.body = { ok: false, msg: '请指明验证码用途' };
      return;
    }

    // 查询当前redis是已经存在这个手机的验证码
    const verifyInRedis = await redis.get(`phone_verify_${mobile}`);
    if (verifyInRedis) {
      ctx.body = { ok: false, msg: '你请求太过频繁，请稍后再试' };
      return;
    }

    // 开始发送短信
    const code = Math.random().toString().slice(-6);
    // 跳过短信验证，默认验证码666666
    if (FAKE_VERTIFICATION) {
      console.log(`发送给手机 ${mobile} 验证码: 666666`);
      redis.set(`phone_verify_${mobile}`, 666666, 'EX', 60);
      ctx.body = { ok: true, msg: '发送短信验证码成功' };
    } else {
      const sendResult = await sendSmsMessage(
        'loginOrRegiste',
        mobile,
        { '#app#': appName || '美景阅读', '#code#': code }
      );
      if (sendResult && sendResult.success) {
        redis.set(`phone_verify_${mobile}`, code, 'EX', 60);
        ctx.body = { ok: true, msg: '发送短信验证码成功' };
      } else {
        ctx.body = { ok: false, msg: sendResult ? sendResult.message : '发送短信验证码失败' };
      }
    }
  });

  /**
   * 发送验证码到当前登录用户手机
   * @parma {String} mobile 手机号码
   * @parma {String} usage 用来登录还是用来注册
   */
  router.post('/api/front/user/mobile_verify', async ctx => {
    const { appName } = ctx.request.body;
    const { userid } = ctx.state.user;
    const currentUser = await User.findById(userid, 'mobile');
    if (!currentUser) {
      await ctx.json(false, '用户不存在');
      return;
    }
    if (currentUser && !currentUser.mobile) {
      await ctx.json(false, '尚未绑定手机');
      return;
    }
    const mobile = currentUser.mobile;

    // 查询当前redis是已经存在这个手机的验证码
    const verifyInRedis = await redis.get(`phone_verify_${mobile}`);
    if (verifyInRedis) {
      redis.del(`phone_verify_${mobile}`);
      // await ctx.json(false, '你请求太过频繁，请稍后再试');
      // return;
    }

    // 开始发送短信
    const code = Math.random().toString().slice(-6);
    // 跳过短信验证，默认验证码666666
    if (FAKE_VERTIFICATION) {
      console.log(`发送给手机 ${mobile} 验证码: 666666`);
      redis.set(`phone_verify_${mobile}`, 666666, 'EX', 60);
      await ctx.json(true, 'success', { mobile });
    } else {
      const sendResult = await sendSmsMessage(
        'loginOrRegiste',
        mobile,
        { '#app#': appName || '美景阅读', '#code#': code }
      );
      if (sendResult && sendResult.success) {
        redis.set(`phone_verify_${mobile}`, code, 'EX', 60);
        await ctx.json(true, 'success', { mobile });
      } else {
        await ctx.json(false, sendResult ? sendResult.message : '发送短信验证码失败');
      }
    }
  });

  /**
   * 验证短信验证码的正确性
   * @parma {String} mobile 手机号码
   * @parma {String} verification 手机验证码
   */
  router.post('/api/front/user/check_verify', async ctx => {
    const { mobile, verification } = ctx.request.body;
    const mobileVerifyReg = /^\d{6}$/;
    if (!verification || !mobileVerifyReg.test(verification)) {
      ctx.body = { ok: false, msg: '手机验证码格式错误' };
      return;
    }

    const verifyInRedis = await redis.get(`phone_verify_${mobile}`);
    if (!verifyInRedis) {
      ctx.body = { ok: false, msg: '验证码已经过期，请重新获取', goback: 1 };
      return;
    }

    if (verifyInRedis && verifyInRedis !== verification) {
      ctx.body = { ok: false, msg: '手机验证码错误' };
      return;
    }

    // 验证成功将验证码的值设置为 verified
    redis.set(`phone_verify_${mobile}_${verification}`, 1, 'EX', 10 * 60);
    ctx.body = { ok: true, msg: '验证ok' };
  });

  /**
   * 重置密码
   * @parma {String} mobile 手机号码
   * @parma {String} verification 手机验证码
   */
  router.post('/api/front/user/change-password', async ctx => {
    const { verification, password } = ctx.request.body;
    const { userid } = ctx.state.user;
    const mobileVerifyReg = /^\d{6}$/;
    if (!verification || !mobileVerifyReg.test(verification)) {
      ctx.body = { ok: false, msg: '手机验证码格式错误' };
      return;
    }
    const currentUser = await User.findById(userid, 'mobile');
    if (!currentUser) {
      await ctx.json(false, '用户不存在');
      return;
    }
    if (currentUser && !currentUser.mobile) {
      await ctx.json(false, '尚未绑定手机');
      return;
    }
    const mobile = currentUser.mobile;

    const verifyInRedis = await redis.get(`phone_verify_${mobile}`);
    if (!verifyInRedis) {
      ctx.body = { ok: false, msg: '验证码已经过期，请重新获取' };
      return;
    }

    if (verifyInRedis && verifyInRedis !== verification) {
      ctx.body = { ok: false, msg: '手机验证码错误' };
      return;
    }

    const savePassword = transPasswordToHash(password);
    const updateResult = await User.updateOne({ _id: userid }, { $set: { password: savePassword } });
    if (updateResult.ok === 1) {
      redis.del(`phone_verify_${mobile}_${verification}`);
      await ctx.json(true, '修改密码成功');
    } else {
      await ctx.json(false, '修改密码失败');
    }
  });

  /**
   * 重置密码
   * @parma {String} mobile 手机号码
   * @parma {String} verification 手机验证码
   */
  router.post('/api/front/user/reset-password', async ctx => {
    const { mobile,verification, password } = ctx.request.body;
    const mobileVerifyReg = /^\d{6}$/;
    const mobileReg = /^1\d{10}$/;

    if (!mobile || !mobileReg.test(mobile)) {
      await ctx.json(false, '请输入正确的手机号码');
      return;
    }

    if (!await User.findOne({ mobile })) {
      await ctx.json(false, '手机号不存在');
      return;
    }

    if (!verification || !mobileVerifyReg.test(verification)) {
      await ctx.json(false, '手机验证码格式错误');
      return;
    }

    if (!password || !validator.isLength(password, { min: 6, max: 30 })) {
      await ctx.json(false, '请输入6到30位的有效密码');
      return;
    }

    const verifyInRedis = await redis.get(`phone_verify_${mobile}`);
    if (!verifyInRedis) {
      ctx.body = { ok: false, msg: '验证码已经过期，请重新获取' };
      return;
    }

    if (verifyInRedis && verifyInRedis !== verification) {
      ctx.body = { ok: false, msg: '手机验证码错误' };
      return;
    }

    const savePassword = transPasswordToHash(password);
    const updateResult = await User.updateOne({ mobile }, { $set: { password: savePassword } });
    if (updateResult.ok === 1) {
      redis.del(`phone_verify_${mobile}_${verification}`);
      await ctx.json(true, '重置密码成功');
    } else {
      await ctx.json(false, '重置密码失败');
    }
  });

  /**
   * 用户注册
   * @parma {String} wey 登录方式，现在支持 miniprogram（小程序）、weixin（微信注册）、mobile（手机注册）
   * wey 为 miniprogram 时
   * @parma {String} code 微信临时登录凭证
   * @parma {String} username 昵称
   * @parma {String} avatar 头像
   * ===========================
   * wey 为 weixin 时
   * 暂无
   * ===========================
   * wey 为 mobile 时
   * @parma {String} mobile 手机号码
   * @parma {String} verification 手机验证码
   * @parma {String} password 登录密码
   */
  router.post('/api/front/user/registe', async ctx => {
    const { wey } = ctx.request.body;
    if (wey === 'miniprogram') {
      // 使用微信小程序注册
      const { code, username, avatar } = ctx.request.body;
      // 向微信服务器发送请求，使用code换取openid和session_key
      const content = querystring.stringify({
        grant_type: 'authorization_code',
        appid: WX_MINIPROGRAM_APP_ID,
        secret: WX_MINIPROGRAM_SECRET,
        js_code: code
      });
      const wxdata = await doRequest('https://api.weixin.qq.com/sns/jscode2session?' + content);
      if (!wxdata || !wxdata.session_key || !wxdata.openid) {
        ctx.body = { ok: false, msg: '微信认证失败' };
        return;
      }

      // 检查用户是否已经存在
      const isUserExited = await User.findOne({ openid: wxdata.openid });
      if (isUserExited) {
        ctx.body = { ok: false, msg: '用户在小程序上已注册' };
        return;
      }

      // 创建用户
      const newUser = await User.create(
        defautUserTemplate({
          username,
          avatar,
          identity: 1, // 区分用户是普通用户还是系统管理员
          openid: wxdata.openid, // 小程序openid
          source: 2 // 微信小程序
        })
      );

      // 生成token签名 有效期为一天
      const token = createToken(newUser, TOKEN_EXPIRED_TIME);

      // 初始化书架
      initUserBooklist(newUser._id);
      console.log('Info', `用户 ${newUser._id} 于 ${newUser.create_time.toDateString()} 注册, 并初始化书架`);
      // 记录日志以及更新头像
      Logger.addLog({ user: newUser._id, name: 'user-registe' });
      updateUserAvatar(newUser._id, avatar, 'image-url');
      ctx.body = {
        ok: true,
        msg: '注册成功',
        token: token,
        userInfo: await formatUserOutput(newUser)
      };
    } else if (wey === 'weixin') {
      // 使用微信注册
    } else if (wey === 'mobile') {
      // 使用手机号码注册
      const { password, mobile, verification } = ctx.request.body;
      let error = {};
      const mobileReg = /^1\d{10}$/;
      const mobileVerifyReg = /^\d{6}$/;
      if (!password || !validator.isLength(password, { min: 6, max: 30 })) error.password = '请输入6到30位的有效密码';

      if (!mobile || !mobileReg.test(mobile)) error.mobile = '请输入正确手机号码';
      if (!verification || !mobileVerifyReg.test(verification)) error.verification = '手机验证码格式错误';
      if (await User.isRepeat('mobile', mobile)) error.mobile = '手机号码已经被注册';

      const verifyInRedis = await redis.get(`phone_verify_${mobile}_${verification}`);
      if (!verifyInRedis) error.verification = '手机核验失败，请重试';

      // 验证是否出错
      if (JSON.stringify(error) !== '{}') {
        ctx.body = { ok: false, error };
        return;
      }

      // 默认生成hash头像
      const username = 'u' + mobile; // 用户名默认使用u+手机号的格式
      const hash = crypto.createHash('md5');
      hash.update(username); // 传入用户名
      const imgData = new Identicon(hash.digest('hex')).toString();
      const avatarBuffer = Buffer.from(imgData, 'base64');
      const avatarKey = `mbook/avatar/${username}.png`;
      try {
        const avatar = await uploadToTencentCos({
          Bucket: 'picture',
          Body: avatarBuffer,
          Type: 'buffer',
          Key: avatarKey
        });
        const newUser = await User.create(
          defautUserTemplate({
            username, // 用户名就使用昵称
            password,
            avatar,
            mobile,
            identity: 1, // 区分用户是普通用户还是系统管理员
            source: 3 // 手机注册
          })
        );

        initUserBooklist(newUser._id);

        if (newUser && newUser.id) {
          // 生成token
          delete newUser.password;
          redis.del(`phone_verify_${mobile}_${verification}`);
          const token = createToken(newUser, TOKEN_EXPIRED_TIME);
          ctx.body = {
            ok: true,
            msg: '用户注册成功',
            token,
            userInfo: await formatUserOutput(newUser)
          };
        } else {
          ctx.body = { ok: false, msg: '用户注册失败' };
        }
      } catch (err) {
        ctx.body = { ok: false, msg: '注册失败，' + err.toString() };
      }
    }
  });

  /**
   * 个人中心页获取个人信息--书币数量、会员信息、已阅读书籍、已邀请人数
   */
  router.get('/api/front/user/info', async (ctx, next) => {
    if (!ctx.state.user) {
      await ctx.noLogin();
      next();
      return;
    }
    const { userid } = ctx.state.user;
    // 用户信息
    const thisUser = await User.findById(userid, 'amount member').populate({
      path: 'member',
      select: 'name expired'
    });
    // 已阅读书籍-后面改成日志统计的方式
    const bookList = await BookList.findOne({ userid }, 'books').populate({
      path: 'books.bookid',
      options: {
        select: 'name img_url'
      }
    });
    // 分享记录
    const share = await Share.findOne({ userid }, 'award_records');
    const awardRecords = share ? share.award_records : [];
    const inviteNums = awardRecords.filter(item => item.name === '邀请别人奖励').length;
    await ctx.json(true, '获取用户信息成功', {
      amount: thisUser.amount,
      member: thisUser.member || {},
      books: bookList ? bookList.books : [],
      share: inviteNums
    });
  });

  /**
   * 获取用户书币数量
   */
  router.get('/api/front/user/amount', async (ctx, next) => {
    if (!ctx.state.user) {
      await ctx.noLogin();
      next();
      return;
    }
    const { userid } = ctx.state.user;
    const thisUser = await User.findById(userid, 'amount member').populate({
      path: 'member',
      select: 'name expired'
    });
    await ctx.json(true, 'success', {
      amount: thisUser.amount,
      member: thisUser.member || {}
    });
  });

  /**
   * 小程序获取个人设置
   */
  router.get('/api/front/user/setting', async (ctx, next) => {
    if (!ctx.state.user) {
      await ctx.noLogin();
      next();
      return;
    }
    const { userid } = ctx.state.user;
    const thisUser = await User.findById(userid);
    const result = {
      updateNotice: !!thisUser.setting.updateNotice,
      autoBuy: !!thisUser.setting.autoBuy,
      reader: {
        fontSize: thisUser.setting.reader.fontSize,
        fontFamily: thisUser.setting.reader.fontFamily,
        bright: thisUser.setting.reader.bright,
        mode: thisUser.setting.reader.mode, // 模式
        overPage: thisUser.setting.reader.overPage || 0 // 翻页模式
      }
    };
    await ctx.json(true, '获取个人信息成功', result);
  });

  /**
   * 前端更新个人设置
   * @param {Object} setting 用户昵称
   */
  router.post('/api/front/user/setting', async (ctx, next) => {
    if (!ctx.state.user) {
      await ctx.noLogin();
      next();
      return;
    }
    const { userid } = ctx.state.user;
    const setting = ctx.request.body.setting;
    if (!setting) {
      await ctx.json(false, '参数错误');
      return;
    }
    const thisUser = await User.findById(userid, 'setting');
    const canUpdateKeys = ['updateNotice', 'autoBuy', 'reader'];
    const newSetting = { ...JSON.parse(JSON.stringify(thisUser.setting)), ...pick(setting, canUpdateKeys) };
    const updateResult = await User.updateOne({ _id: userid }, { $set: { setting: newSetting } });
    if (updateResult.ok === 1) {
      await ctx.json(true, '更新设置成功');
    } else {
      await ctx.json(false, '更新设置失败');
    }
  });

  /**
   * 前端绑定手机号码
   * @param {Object} setting 用户昵称
   */
  router.post('/api/front/user/bind-mobile', async (ctx, next) => {
    if (!ctx.state.user) {
      await ctx.noLogin();
      next();
      return;
    }
    // 使用手机号码注册
    const { type, password, mobile, verification } = ctx.request.body;
    const mobileReg = /^1\d{10}$/;
    const mobileVerifyReg = /^\d{6}$/;

    // 需要校验的字段
    let validateKyes = ['mobile', 'verification'];
    if (type === 'bind-and-set-password') {
      validateKyes = ['mobile', 'verification', 'password'];
    }

    // 数据校验
    const error = {};
    if (validateKyes.indexOf('mobile') > -1) {
      if (!mobile || !mobileReg.test(mobile)) error.mobile = '请输入正确的手机号码';
      if (await User.isRepeat('mobile', mobile)) error.mobile = '手机号码已经被绑定';
    }

    if (validateKyes.indexOf('password') > -1 &&
      (!password || !validator.isLength(password, { min: 6, max: 30 }))
    ) error.password = '请输入6到30位的有效密码';

    if (validateKyes.indexOf('verification') > -1) {
      if (!verification || !mobileVerifyReg.test(verification)) error.verification = '手机验证码格式错误';
      const verifyInRedis = await redis.get(`phone_verify_${mobile}_${verification}`);
      if (!verifyInRedis) error.verification = '手机核验失败，请重试';
    }


    // 验证是否出错
    if (JSON.stringify(error) !== '{}') {
      ctx.body = { ok: false, error };
      return;
    }

    const { userid } = ctx.state.user;

    const newData = { mobile };
    if (type === 'bind-and-set-password') newData.password = transPasswordToHash(password);

    const updateResult = await User.updateOne({ _id: userid }, { $set: newData });
    if (updateResult.ok === 1) {
      redis.del(`phone_verify_${mobile}_${verification}`);
      await ctx.json(true, '绑定手机成功');
    } else {
      await ctx.json(false, '绑定手机失败');
    }
  });

  /**
   * 后台获取用户列表
   * @param {String} name 用户昵称
   * @param {String} userid 用户ID
   * @param {String} page
   * @param {String} limit
   */
  router.get('/api/admin/user', async ctx => {
    const { name, userid, mobile, sort_key: sortKey, sort_direction: sortDirection } = ctx.request.query;
    let queryParams = {};
    if (name) queryParams.username = new RegExp(name, 'i');
    if (userid) queryParams._id = ctx.transId(userid);
    if (mobile) queryParams.mobile = mobile;
    queryParams.identity = 1;
    const { page, limit } = ctx.parsePageAndLimit(ctx.request.query);
    const total = await User.countDocuments(queryParams);
    const users = await User.find(queryParams)
      .skip((page - 1) * limit)
      .limit(limit)
      .sort({ [sortKey]: sortDirection })
      .populate({
        path: 'member',
        select: 'name expired'
      });

    await ctx.json(true, '搜索用户成功', { list: users, total });
  });

  /**
   * 后台获取用户画像
   * @param {String} userid 用户ID
   */
  router.get('/api/admin/user/info', async ctx => {
    const { userid } = ctx.request.query;
    // 基本信息
    const thisUser = await User.findById(userid, '-password').populate({
      path: 'member',
      select: 'name expired'
    });
    // 阅读记录
    const bookList = await BookList.findOne({ userid }, 'books').populate({
      path: 'books.bookid',
      options: {
        select: 'name img_url update_status update_time newest_chapter'
      }
    });
    // 评论信息
    const comment = await Comment.find({ userid }, 'content create_time');
    // 分享记录
    const share = await Share.find({ userid }, 'code award_records accept_records');

    await ctx.json(true, '获取用户画像成功', {
      user: thisUser,
      bookList: bookList ? bookList.books : [],
      comment,
      share
    });
  });

  /**
   * 后台更新用户
   * @param {String} id 用户ID
   * @param {String} amount 用户书币
   */
  router.put('/api/admin/user/:id', async ctx => {
    const { amount, remark } = ctx.request.body;
    const id = ctx.params.id;
    const updateParams = {};
    if (amount) updateParams.amount = +amount;
    if (remark) updateParams.remark = remark;
    const result = await User.updateOne({ _id: id }, { $set: updateParams });
    if (result.ok === 1) {
      await ctx.json(true, '更新用户成功');
    } else {
      await ctx.json(false, '更新用户失败');
    }
  });

  /**
   * 后台删除用
   * @param {String} id 用户ID
   */
  router.delete('/api/admin/user/:id', async ctx => {
    const id = ctx.params.id;
    // 删除其他用户关联的表
    await Award.deleteMany({ userid: id });
    await Comment.deleteMany({ userid: id });
    await BookList.deleteMany({ userid: id });
    await Pay.deleteMany({ userid: id });
    await Share.deleteMany({ userid: id });
    await Attendance.deleteMany({ userid: id });
    await Member.deleteMany({ user: id });
    await Logger.deleteMany({ user: id });
    await User.deleteMany({ _id: id });
    await ctx.json(true, '删除用户成功');
  });
}
